home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
ZSIM12.ARJ
/
CPMBIOS.ASM
< prev
next >
Wrap
Assembly Source File
|
1992-01-12
|
73KB
|
3,167 lines
;
; BIOS FUER DEN CP/M EMULATOR
;
;
; (C) 1990,1991,1992 by Jürgen Weber
;
; Version: 1.0
; 1.1 Disparameter laden/speichern
; 1.11 bios read zeigt Dos File Ende an
; 1.2 conin übersetzt CsrTasten nach WS
; ergänzte time fkt und Blink Attribut
INCLUDE Z80EMU.INC
EXTRN conout:near,conin:near,reset_conout:near,sel_scr_page:near
EXTRN cursor_off:near,cursor_on:near
PUBLIC prg_exit,bios88
PUBLIC breakflag
%nosyms ; keine symbols im listing
warn ; alle warnings an
DOSSEG ; UNBEDINGT notwendig, da fuer Speicherverwaltung des Z80 Segments
; und der Ramdisk Stacksegment als letztes kommen muss
; auch mussen Stack- und Codesegment Klassen 'STACK' bzw.
; 'CODE'bekommen
LOCALS ; fuer locale Labels in Prozeduren
JUMPS ; automatische Sprunglaengenanpassung
CALLZ macro label ; Call if zero
local exit
jnz short exit
call label
exit:
endm
CALLNZ macro label ; Call if not zero
local exit
jz short exit
call label
exit:
endm
CALLC macro label ; Call if carry
local exit
jnc short exit
call label
exit:
endm
;
; segmente des emulators:
;
; emulator_seg : emulator und bios code
; emudata_seg : emulator data und z80 bios code
; stack_seg : emulator und bios stack
; z80cpu_seg : Segment des emulierten z80, 64k, dummy at 0 (in z80seg_adr)
; ramdisc_seg : ramdisc fuer cp/m
;
include dos.inc ; dos functionen
;
; ******* KONSTANTEN ********
;
DEFAULT_DMA EQU 80H
RECORD_LEN EQU 80H
ZBIOSLEN equ (zbiosend-zbiosbeg)
ZBIOSDISPL equ (0ffffh-ZBIOSLEN) and 0ff00h
; dies bewirkt Start auf neuer Seite, damit Bios auf XX00 anfaengt
ZBIOS equ offset zbiosbeg+ZBIOSDISPL
CTRL_BREAK_INT EQU 1BH
ESC_KEY EQU 27
EMU_SCR_PAGE EQU 0
DOS_SCR_PAGE EQU 1
; folgende Konstanten werden als Flags zur Addressberechnung
; im Sektorbuffer benoetigt
READ_OP EQU 0
WRITE_OP EQU 0FFH
PHYSDSK EQU 0
BDOSLEN equ 1600h ; Laenge ccp+bdos cp/m 2.2
BDOS_CHK_SUM equ 2d88h ; Checksumme ueber Bdos Copyright Meldung
SYS_FRST_SEC EQU 28
; erster absoluter Sektor, den Bdos auf SysSpuren belegt
SYS_SECS EQU BDOSLEN/RECORD_LEN ; Anzahl Sektoren, die System belegt
TRACK_BUF_LEN EQU 10*512
CPM_EOF EQU 26
PHYS_DRV EQU 0 ; # diskettenlaufwerk
RMD equ 1 ; # ramdisk
PHYS_SEKLEN equ 512
NSECTS equ 2
NDISKS equ 2 ; last disk # +1
BPS equ 2
; -------------------------------------
;
; emulator data segment
;
; --------------------------------------
emudata_seg segment para public 'DATA'
include zbios.inc
dpb_cpc dpb <SPTC,BSHC,BLMC,EXMC,DSMC,DRMC,AL0C,AL1C,CKSC,OFFC>
dpb_dta dpb <SPTD,BSHD,BLMD,EXMD,DSMD,DRMD,AL0D,AL1D,CKSD,OFFD>
dpb_eig dpb <SPTD,BSHD,BLMD,EXMD,DSMD,DRMD,AL0D,AL1D,CKSD,OFFD>
dpb_cpm86SS dpb <SPT0,BSH0,BLM0,EXM0,DSM0,DRM0,AL00,AL10,CKS0,OFF0>
dpb_cpm86DS dpb <SPT1,BSH1,BLM1,EXM1,DSM1,DRM1,AL01,AL11,CKS1,OFF1>
ramdisklen dw (?)
lasttrack_rd db (?) ; 0..127 (512K)
PUNCH_BUF_SIZE equ 512
READER_BUF_SIZE equ 256
start_string db 27,'E',27,'Y',32+8,32+24,27,'p'
db 'ZSIM',27,'q'
db 27,'Y',32+10,32+18
db 'Free for personal use'
db 27,'Y',32+12,32+18
db 27,'p','USE AT YOUR OWN RISC !',27,'q'
db 27,'Y',32+15,32+18
db '(C) 1990,1992 by '
db 27,'Y',32+17,32+18
db 'Jürgen G. Weber'
db 27,'Y',32+18,32+18
db 'Wiesentalstr. 1'
db 27,'Y',32+19,32+18
db 'W-7170 Schwäbisch Hall'
db 27,'Y',32+20,32+18
db 'Federal Republic of Germany'
db 27,'Y',32+24,32+17,27,'p'
db 'Insert a CP/M disk then press any key'
db 27,'q'
db 0
db 'ZSIM looks for the fellowing string: '
bdos_str db ' COPYRIGHT (C) 1979, DIGITAL RESEARCH '
bdos_str_end equ this byte
cpmsys_fileName db 'CPMSYS.CPM',0
ramdisc_fname db 'RAMDISC.CPM',0
punch_fileName db 'PUNCH.CPM',0
reader_fileName db 'READER.CPM',0
temp_fname db '\EMUCPM.OVL',0
fmt_file_flg db FALSE
fmt_file_name db 80 dup (?)
file_promt_str db 'Enter Disk Parameter filename: ',0
file_promt_str_end equ $
menutab label word
dw m_esc
dw m_continue
dw m_save_rd_quit
dw m_quit
dw m_save_rd
dw m_del_pf
dw m_init_rdr
dw m_shell
dw m_edit_disk_parm
dw m_save_d_parm
dw m_load_d_parm
dw m_w_boot
dw m_info
menu_strs db '\Continue','|'
db 'Save \Ramdisc/Quit','|'
db '\Quit','|'
db '\Save Ramdisc','|'
db '\Delete Punch File','|'
db '\Init Reader','|'
db '\OS Shell','|'
db 'Disk \Parameters','|'
db 'Sa\ve Parameters','|'
db '\Load Parameters','|'
db '\Warm Boot','|'
db '\About','|'
db 0
menu_strs_end equ this byte
MESG_ESC equ 0
LONGEST_MESSAGE=20
MESSAGE_COUNT=12
prg_ext_box_res dw 0ffffh
ins_cpm_disk_txt db 'Insert CP/M disk and press ENTER|',0
ins_cpm_disk_txt_end equ this byte
ins_dos_disk_txt db 'Insert MS-DOS disk and press ENTER|',0
ins_dos_disk_txt_end equ this byte
disk_err_txt db 'Disk Error. Press ESC|',0
disk_err_txt_end equ this byte
disk_ful_txt db 'Disk full. Press ESC|',0
disk_ful_txt_end equ this byte
f_not_found_txt db 'File not found. Press ESC|',0
f_not_found_txt_end equ this byte
on_sign_string equ this byte
db 27,'E',27,'H'
db 'jgw 64K cp/m 80 bios ver 1.2 -- '
db ??date
db ' (C) 1990,1992 by Jürgen G. Weber'
db 13,10,10
db 'BDOS: ',27,'j',0
bad_format db 13,10
db 'Unknown disc format. Insert new disc and press any key'
db 13,10,0
no_sys db 13,10
db 'Could not read System sectors. Insert'
db ' new disc and press any key'
cr_lf_txt db 13,10,0
control_c_txt db '^C...',0
mfulstr db 13,10,'Not enough memory',13,10
mfsend equ this byte
illparm_str db 13,10,'Illegal Command Line Parameter',13,10
illparm_strend equ this byte
dma_txt db 13,10
db 'Fatal: DMA Boundary Crossing'
db 13,10
dma_txt_end equ this byte
exit_str db 'Type EXIT to return to CP/M Emulator ...',13,10
exit_str_end equ this byte
exec_par_block equ this word
dw 0
dw offset exec_cmd_line
dw seg exec_cmd_line
dd 0
dd 0
exec_cmd_line equ this word
db ec_str_end-ec_str
ec_str db ''
ec_str_end equ this byte
db 13
no_cc_str db 13,10,'COMMAND.COM not found. Press ESC.',13,10
no_cc_str_end equ this byte
exec_fname db '\COMMAND.COM',0
good_dpb_edit db (?)
db 2 dup (?)
breakflag db FALSE
old_int1b dd (?)
stackpoi dw (?)
sp_save dw (?)
ss_save dw (?)
z80seg_adr dw (?)
rdseg_adr dw (?)
rdlen dw (?)
exec_mem_start dw (?)
psp_adr dw (?)
prog_len dw (?)
mem_end dw (?)
ovl_base dw (?)
hd_drive_flag db (?)
ms_current_drive db (?)
tmp_word dw (?)
z80_pc dw ?
ccp_adr dw ?
bdos_adr dw ?
bios_adr dw ?
month_tab db 31,28,31,30,31,30,31,31,30,31,30,31
; flag, dass im Speicher noch ein nicht auf disk geschriebener
; nicht-dir Track ist
write_flag db FALSE
in_boot_flag db FALSE
home_flag db FALSE
last_track_written db 0,0
dirtrack db 0,0
r_track db 0,0
w_track db 0,0
track db 0,0
sector db 0,0
last_track_read db 0ffh ; default unmoeglich, noch nicht gelesen
ms_phys_sec_len db (?)
pspt_last db 9
phys_tracks_last db 40
frstps_last db 041h
dirtr_last db 2
dpb_last dw offset dpb_cpc
cpm86dd_flag db FALSE
; ACHTUNG: die Reihenfolge der folgenden Vars nicht
; verändern, da sie so ans Modula UP übergeben werden
cpm_drive db PHYS_DRV
first_phys_sec db 41h
phys_tracks db (?)
phys_sec_pt db (?)
cpm_phys_sec_len db 2
autologin_flag db TRUE
; end Reihenfolge wichtig
retry_count db 5
side db 0
dmaad dw ? ;direct memory address
diskno db ? ;disk number 0-15
punch_buf_entries dw (?)
punch_buf_ptr dw (?)
reader_empty_flag db TRUE
reader_buf_entries dw (?)
reader_buf_ptr dw (?)
reader_file_pointer dw 0,0
; zeigt auf Bufferadresse des Tracks, der geschrieben werden soll
outbuf_ptr dw (?)
cpm_bdos_buf db BDOSLEN dup (?)
dirtrbuf db TRACK_BUF_LEN dup (?)
secbuf db TRACK_BUF_LEN dup (?)
wrtbuf db TRACK_BUF_LEN dup (?)
punch_buf db PUNCH_BUF_SIZE dup (?)
reader_buf db READER_BUF_SIZE dup (?)
; dass die Buffer in der EXE-Datei erscheinen, laesst sich
; durch die Verwendung einer Gruppe mit den Buffern als
; Extra Segment umgehen
; jedoch muss dann bei jedem offset der Gruppenname angegeben werden
emudata_seg ends
; ------------------------
DATA segment para public 'DATA'
; Dummysegment zur Kombinierung mit Modula 2
DATA ends
STACK_SIZE EQU 1000H
stack_seg segment para stack 'STACK'
dw STACK_SIZE dup (?) ; Stack ist recht gross
; wegen Modula 2 UPs
stack_seg ends
z80cpu_seg segment at 0 ; dummy
z80cpu_seg ends
; -------------------------------------
; emulator und bios segment start
; -------------------------------------
emulator_seg segment para public 'CODE'
assume cs:emulator_seg,ds:emudata_seg,es:z80cpu_seg,ss:stack_seg
m2_ds dw (?) ; fmodula routinen erwarten
; bei cs:0 ihr DS
init proc ; emulator start
mov ax,emudata_seg
mov ds,ax ; damit Vars angesprochen werden koennen
mov stackpoi,sp
mov bx,ss ; finde Programmende
mov ax,sp
mov cl,4
shr ax,cl ; ax:=4
add bx,ax ; bx=>programm ende
inc bx ; vorsichtshalber
push bx
mov ax,es ; es:0 => PSP
mov psp_adr,ax
mov bx,ax
mov si,0
mov ax,[es:si+2] ; ax=> mem end
mov mem_end,ax
push ax
sub ax,1000h
mov ovl_base,ax
pop ax
push ax
sub ax,bx ; mem end - prg start
mov prog_len,ax
pop ax ; mem end
pop bx ; prg end
sub ax,bx ; ax:=free mem
mov z80seg_adr,bx
mov cx,10000h / 16
add bx,cx
mov rdseg_adr,bx
sub ax,cx
jc short @@memful
mov ramdisklen,ax
call parse_cmd_line
mov dx,offset illparm_str
mov cx,illparm_strend-illparm_str
jc short abort ; Fehler: falscher Parameter
mov bx,offset start_string
call puts
call conin ; wait for keypressed
call patch_int
DOS GET_DISK_DRIVE
mov ms_current_drive,al
mov ax,z80seg_adr
call clearz80 ; loesche z80 mem
call init_ramdisc
call patch_box
; physikalische Sektorlaenge merken
push es
mov ax,0
mov es,ax
les di,[es:78h]
mov al,es:[di+3]
pop es
mov ms_phys_sec_len,al
;
; offset z80 bios start merken
;
mov bios_adr,ZBIOSDISPL ; bios Sprungleiste einrichten
call z80ini ; reset z80 cpu
mov ax,z80seg_adr
mov es,ax
mov ds,ax
;
; weiter mit cp/m cold boot
;
mov al,0
jmp bios88
@@memful:
mov cx,mfsend-mfulstr
mov dx,offset mfulstr
abort:
mov bx,STDERR
DOS WRITE_TO_HANDLE
mov al,1
DOS TERMINATE_EXE
init endp
parse_cmd_line proc
PUSHR <ax,bx,cx,di,si,ds,es>
mov ax,ds
mov es,ax ; es:=prog vars
mov ax,psp_adr
mov ds,ax
mov di,offset fmt_file_name
mov si,80h ; si => Commandozeilenparam
lodsb ; count
mov ch,0
mov cl,al
or al,al
mov al,FALSE
jz short @@no_parm
@@findslash:
lodsb
cmp al," "
loopz @@findslash ; +DEC CX
cmp al,"/"
jnz short @@parm_err
lodsb
dec cx
and al,not ('a'-'A') ; toupper
cmp al,'F'
jnz short @@parm_err
mov di,offset fmt_file_name
@@SKIP_BLN:
lodsb
cmp al," "
loopz @@SKIP_BLN; +DEC CX
stosb ; 1. non-blank
rep movsb
mov al,0
stosb ; Filename Endzeichen
mov al,TRUE
@@no_parm:
POPR <es,ds,si,di,cx,bx>
mov fmt_file_flg,al
pop ax
clc
ret
@@parm_err:
POPR <es,ds,si,di,cx,bx,ax>
stc
ret
parse_cmd_line endp
tst_hd_drive proc
PUSHR <ax,dx>
mov hd_drive_flag,FALSE
mov dl,cpm_drive
mov ah,15h ; Laufwerktyp ?
int 13h
jc short @@exit ; PC,XT
cmp ah,2 ; erkennt DiskWechsel
jnz short @@exit
mov hd_drive_flag,TRUE
@@exit:
POPR <dx,ax>
ret
tst_hd_drive endp
;
; der Interupt 1bH (Control-Break Interupt) muß auf eine Routine
; gelegt werden, die ein Abbruch Flag setzt
; Dieses wird dann bei jedem Bios Aufruf ausgewertet
;
patch_int proc
PUSHR <ds,es,ax,bx,dx>
mov al,CTRL_BREAK_INT
DOS GET_VECTOR
mov word ptr old_int1b,bx
mov bx,es
mov word ptr old_int1b+2,bx
mov dx,offset new_int1b
push cs
pop ds
mov al,CTRL_BREAK_INT
DOS SET_VECTOR
POPR <dx,bx,ax,es,ds>
ret
patch_int endp
restore_int proc
push ds
lds dx,old_int1b
mov al,CTRL_BREAK_INT
DOS SET_VECTOR
pop ds
ret
restore_int endp
new_int1b proc
push ax
push ds
mov ax,emudata_seg
mov ds,ax
mov breakflag,TRUE
pop ds
pop ax
iret
new_int1b endp
; wird auch von Z80EMU.OP76 HALT aufgerufen
prg_exit proc FAR
PUSHR <ds,es,ax>
PUSHR <bx,cx,dx,si>
mov ax,emudata_seg
mov ds,ax
mov ax,z80seg_adr
mov es,ax
call restore_int
mov breakflag,FALSE
call wrt_out_punch
mov cx,menu_strs_end-menu_strs ; high(messages)
mov si,offset menu_strs ; offs(messages)
call box_call
sal ax,1
mov di,ax
call menutab[di]
call patch_int
POPR <si,dx,cx,bx>
POPR <ax,es,ds>
ret
prg_exit endp
; Menü durch ESC abgebrochen
m_esc proc
ret
m_esc endp
; Menüpunkt Emulation fortfahren
m_continue proc
ret
m_continue endp
; Menüpunkt Ramdisk speichern
m_save_rd proc
call save_ramdisc
ret
m_save_rd endp
; Menüpunkt Quit
m_quit proc
; etwaige Zeichen im Punch Buffer noch ausschreiben
call wrt_out_punch
mov cl,31 ; GotoXY(1,25)
call conout
mov cl,1
call conout
mov cl,25
call conout
mov al,0
ABORT_EMU:
push ax
call ins_dos_disk
pop ax
DOS TERMINATE_EXE
m_quit endp
; Menüpunkt Ramdisk speichern + Quit
m_save_rd_quit proc
call save_ramdisc
jmp short m_quit
m_save_rd_quit endp
; Menüpunkt Punch buffer file löschen
m_del_pf proc
call del_punch_file
ret
m_del_pf endp
; Menüpunkt Reader initialisieren
m_init_rdr proc
call reset_reader_buf
ret
m_init_rdr endp
; Menüpunkt OS Shell
m_shell proc
call dos_shell
ret
m_shell endp
; Menüpunkt Diskettenparameter editieren
m_edit_disk_parm proc
call edit_disk_pars
cmp good_dpb_edit,1
jnz short @@exit
mov cpm86dd_flag,FALSE
mov ah,0
mov al,dpb0.off-dpb0.spt ; off
call get_dpb_entrie ; nach bx
mov dirtrack,bl
cmp in_boot_flag,TRUE
jz short @@exit ; mit boot weitermachen
call m_w_boot
@@exit:
ret
m_edit_disk_parm endp
prepare_fname proc
mov di,offset fmt_file_name
push di
mov cx,80
mov si,offset file_promt_str
mov bx,file_promt_str_end-file_promt_str
call input_string
pop bx
or byte ptr [bx],0 ; Test ob was eingegeben, nein => Z
ret
prepare_fname endp
; Menüpunkt Diskettenparameter speichern
m_save_d_parm proc
call prepare_fname
jz short @@exit
call save_disk_pars
@@exit:
ret
m_save_d_parm endp
; Menüpunkt Diskettenparameter laden
m_load_d_parm proc
call prepare_fname
jz short @@exit
call load_disk_pars
jmp short m_w_boot
@@exit:
ret
m_load_d_parm endp
; Menüpunkt CP/M warm boot durchführen
m_w_boot proc
mov sp,stackpoi
call patch_int
mov al,1
jmp bios88
m_w_boot endp
; Menüpunkt About Author
m_info proc
call about_author
ret
m_info endp
; Fehler durch Ueberschreiten der DMA Segment Grenze
; siehe c't 4/90 S.412
dma_bound proc
mov bx,STDERR
mov cx,dma_txt_end-dma_txt
mov dx,offset dma_txt
DOS WRITE_TO_HANDLE
mov al,1
jmp ABORT_EMU
dma_bound endp
; out: ax=result
ins_dos_disk proc
mov al,ms_current_drive
cmp al,cpm_drive
jnz short @@exit
mov si,offset ins_dos_disk_txt
mov cx,ins_dos_disk_txt_end-ins_dos_disk_txt
call box_call
@@exit:
ret
ins_dos_disk endp
; out: ax=result
ins_cpm_disk proc
mov al,ms_current_drive
cmp al,cpm_drive
jnz short @@exit
mov si,offset ins_cpm_disk_txt
mov cx,ins_cpm_disk_txt_end-ins_cpm_disk_txt
call box_call
@@exit:
ret
ins_cpm_disk endp
disp_disk_err proc
PUSHR <si,cx>
pushf ; cy aufheben
mov si,offset disk_err_txt
mov cx,disk_err_txt_end-disk_err_txt
call box_call
POPR <cx,si>
popf
ret
disp_disk_err endp
disp_f_not_found proc
pushf ; cy aufheben
PUSHR <si,cx>
mov si,offset f_not_found_txt
mov cx,f_not_found_txt_end-f_not_found_txt
call box_call
POPR <cx,si>
popf
ret
disp_f_not_found endp
disp_disk_ful proc
PUSHR <si,cx>
mov si,offset disk_ful_txt
mov cx,disk_ful_txt_end-disk_ful_txt
call box_call
POPR <cx,si>
ret
disp_disk_ful endp
EXTRN EmuMenu_DoMenue:far,EmuMenu_EditDPB:far
EXTRN EmuMenu_InputString:far,EmuMenu_About:far,EmuMenu_init:far
; fmodula2 laedt merkwuerdigerweise am Anfang DS aus CS:0
; also muss man dort ds hinbringen
; Trotzdem sollte vor Aufruf einer Funktion DS richtig
; geladen sein.
patch_box proc
mov ax,DATA
mov [cs:0],ax
ret
patch_box endp
box_call proc ; um Modula 2 UPs aufzurufen
;
; in: ds:si => menuetext
; cx = len(menuetext)
; out: ax = result
;
; auf dem STACK muss GENUG FREI sein, um zu erzeugendes WINDOW
; ABZUSPEICHERN
;
COMMENT @
DEFINITION MODULE EmuM2;
FROM SYSTEM IMPORT BYTE,WORD;
TYPE PhysDiskPars = RECORD
cpm_drive : BYTE;
first_phys_sec : BYTE;
phys_tracks : BYTE;
phys_sec_pt : BYTE;
bytes_per_sec : BYTE;
autologin_flag : BYTE;
END;
dpb = RECORD
spt : WORD;
bsh : BYTE;
blm : BYTE;
exm : BYTE;
dsm : WORD;
drm : WORD;
al0 : BYTE;
al1 : BYTE;
cks : WORD;
off : WORD;
END;
DPBPtr = POINTER TO dpb;
PDPPtr = POINTER TO PhysDiskPars;
PROCEDURE DoMenue(x,y:CARDINAL;messages:ARRAY OF CHAR;
VAR WinSave:ARRAY OF CHAR;VAR res:CARDINAL);
(* Pop Up Menue, obere linke Ecke des Rahmens bei x,y.
messages = String mit darzustellenden Menuepunkten; Stringende = 0C.
Menuepunkte werden durch | getrennt, auch nach letztem Menuepunkt
MUSS | stehen.
Jeder Menuepunkt darf durch Druecken eines einzigen Zeichens angewaehlt
werden, das hell dargestellt wird. Diesem Zeichen muss der Backslash \
vorangestellt werden.
Es duerfen maximal 20 Menuepunkte sein.
WinSave muss genug Platz enthalten, um Hintergrund + dessen Attribute
abzuspeichern, = (Laengster Menuepunkt+2)*2*(Menuepunkte+2)
*)
PROCEDURE EditDPB(p:DPBPtr;q:PDPPtr;
VAR WinSave:ARRAY OF CHAR;VAR OK:BOOLEAN);
(* Len WinSave=20*18*2=720 *)
PROCEDURE InputString(VAR WinSave,s,p:ARRAY OF CHAR);
(* Es wird vorrausgesetzt, daß prompt p < 30 und string s < 30 *)
PROCEDURE About(VAR WinSave:ARRAY OF CHAR);
(* Author ausgeben *)
END EmuM2.
@
HIGH_WinSave=(LONGEST_MESSAGE+2)*2*(MESSAGE_COUNT+2)
IF HIGH_WinSave gt 2*STACK_SIZE/3
%out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov ax,20
push ax ; x
mov ax,7
push ax ; y
push cx ; high(messages)
push ds ; seg(messages)
push si ; offs(messages)
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
mov ax,seg prg_ext_box_res
push ax ; seg(prg_ext_box_res)
mov ax,offset prg_ext_box_res
push ax ; offs(prg_ext_box_res)
;box_adr equ this byte
mov ax,DATA
mov ds,ax
call EmuMenu_DoMenue
add sp,HIGH_WinSave
POPR <es,ds>
mov ax,prg_ext_box_res
POPR <bp,si,di,dx,cx,bx>
ret
box_call endp
edit_disk_pars proc
HIGH_WinSave=800
IF HIGH_WinSave gt 2*STACK_SIZE/3
%out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov ax,z80seg_adr
push ax ; seg(p) p:DPBPtr
mov ax,offset dpb0+ZBIOSDISPL
push ax ; offs(p)
mov ax,seg cpm_drive
push ax ; seg (q) q:PDPPtr;
mov ax,offset cpm_drive
push ax ; offs (q)
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
mov ax,seg good_dpb_edit
push ax ; seg(ok)
mov ax,offset good_dpb_edit
push ax ; offs(ok)
mov ax,DATA
mov ds,ax
call EmuMenu_EditDPB
add sp,HIGH_WinSave
POPR <es,ds,bp,si,di,dx,cx,bx>
ret
edit_disk_pars endp
;PROCEDURE InputString(VAR WinSave,s,p:ARRAY OF CHAR);
input_string proc ; ds:si = prompt, bx = len prompt
; ds:di = string cx = len string
LOCAL prompt_str,lenpr,in_str,lenstr:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
mov prompt_str,si
mov in_str,di
mov lenpr,bx
mov lenstr,cx
HIGH_WinSave=800
IF HIGH_WinSave gt 2*STACK_SIZE/3
%out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov ax,z80seg_adr
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
push lenstr ; high(s)
push ds ; seg(s)
push in_str ; offs(s)
push lenpr ; high(p)
push ds ; seg(p)
push prompt_str ; offs(p)
mov ax,DATA
mov ds,ax
call EmuMenu_InputString
add sp,HIGH_WinSave
POPR <es,ds,bp,si,di,dx,cx,bx>
mov sp,bp
pop bp
ret
input_string endp
load_disk_pars proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call ins_dos_disk
call @@fopen
jnc short @@ok
cmp ax,2
CALLZ disp_f_not_found
jc short @@exit ; nicht da
@@ok:
call @@fread
call @@fclose
mov autologin_flag,FALSE
@@exit:
call ins_cpm_disk
mov sp,bp
pop bp
ret
@@fopen:
mov al,0
mov dx,offset fmt_file_name
DOS OPEN_FILE
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fread:
mov dx,offset tmp_word
mov bx,handle
mov cx,2
DOS READ_FROM_HANDLE
CALLC disp_disk_err
mov bx,dx
mov bx,[bx]
cmp bx,'WJ'
jnz short @@ldexit ; falscher Kenncode <> 'JW'
; cp/m Parameter laden
mov dx,offset offset dpb0+ZBIOSDISPL
mov bx,handle
mov cx,size dpb
push ds
mov ax,z80seg_adr
mov ds,ax
DOS READ_FROM_HANDLE
pop ds
CALLC disp_disk_err
; physikalische Parameter laden
mov dx,offset cpm_drive
mov bx,handle
mov cx,autologin_flag-cpm_drive
DOS READ_FROM_HANDLE
CALLC disp_disk_err
@@ldexit:
ret
load_disk_pars endp
save_disk_pars proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call ins_dos_disk
cmp ax,MESG_ESC
jz short @@exit
call @@fopen
jc short @@exit
call @@fwrite
call @@fclose
call ins_cpm_disk
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov cx,0
mov dx,offset fmt_file_name
DOS CREATE_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fwrite:
; erst Kennung schreiben
mov bx,offset tmp_word
mov [bx],'WJ'
mov dx,bx
mov bx,handle
mov cx,2
DOS WRITE_TO_HANDLE
CALLC disp_disk_ful
; cp/m Parameter schreiben
mov dx,offset offset dpb0+ZBIOSDISPL
mov bx,handle
mov cx,size dpb
push ds
mov ax,z80seg_adr
mov ds,ax
DOS WRITE_TO_HANDLE
pop ds
CALLC disp_disk_err
; physikalische Parameter schreiben
mov dx,offset cpm_drive
mov bx,handle
mov cx,autologin_flag-cpm_drive
DOS WRITE_TO_HANDLE
CALLC disp_disk_err
ret
save_disk_pars endp
about_author proc
PUSHR <bx,cx,dx,di,si,bp,ds,es>
sub sp,HIGH_WinSave
mov bx,sp
mov cx,HIGH_WinSave
push cx ; high(WinSave)
push ss ; seg(WinSave)
push bx ; offs(WinSave)
mov ax,DATA
mov ds,ax
call EmuMenu_About
add sp,HIGH_WinSave
POPR <es,ds,bp,si,di,dx,cx,bx>
ret
about_author endp
clearz80 proc ; loesche z80mem
;
; input: ax=seg z80 segment
; alle register ok
;
push es
PUSHR <ax,cx,di>
mov es,ax
mov cx,8000h ; 32k words
mov di,0
mov ax,0
rep stosw ; es:di := 0000h
POPR <di,cx,ax>
pop es
ret
clearz80 endp
dos_exec proc
mov al,DOS_SCR_PAGE
call sel_scr_page
mov bx,STDOUT
mov cx,exit_str_end-exit_str
mov dx,offset exit_str
DOS WRITE_TO_HANDLE
PUSHR <bx,cx,dx,di,si,bp,es>
mov sp_save,sp
mov ss_save,ss
mov bx,seg exec_par_block
mov es,bx
mov bx,offset exec_par_block
mov dx,offset exec_fname
mov ax,seg exec_fname
mov ds,ax
mov al,EMU_SCR_PAGE
DOS EXEC ; ax:=error code
mov bx,emudata_seg
mov ds,bx
cli
mov ss,ss_save
mov sp,sp_save
sti
POPR <es,bp,si,di,dx,cx,bx>
cmp ax,2
jnz short @@cc_found
mov bx,STDERR
mov cx,no_cc_str_end-no_cc_str
mov dx,offset no_cc_str
DOS WRITE_TO_HANDLE
@@press_esc:
call conin
cmp al,ESC_KEY
jnz short @@press_esc
@@cc_found:
mov al,0
call sel_scr_page
ret
dos_exec endp
dos_shell proc
LOCAL handle:WORD,mem_adr:WORD = AUTO_SIZE
PUSHR <ax,bx,cx,dx,si,di>
push bp
mov bp,sp
sub sp,AUTO_SIZE
call ins_dos_disk
cmp ax,MESG_ESC
jz short @@exit ; ESC
call @@fcreate
jc short @@exit
call @@fwrite
call @@fclose
call @@set_free
call dos_exec
call @@get_mem
call @@fopen
call @@fread
call @@fclose
mov dx,offset temp_fname
DOS DELETE_FILE
CALLC disp_disk_err
call ins_cpm_disk
@@exit:
mov sp,bp
pop bp
POPR <di,si,dx,cx,bx,ax>
ret
@@set_free:
push es
mov es,psp_adr
mov bx,prog_len
sub bx,1000h
DOS MODIFY_MEMORY
mov bx,mem_end
sub bx,1000h
mov ovl_base,bx
pop es
ret
@@get_mem:
push es
mov es,psp_adr
mov bx,prog_len
DOS MODIFY_MEMORY
pop es
ret
@@fcreate:
mov cx,2 ; Hidden
mov dx,offset temp_fname
DOS CREATE_FILE
CALLC disp_disk_err ; Abbruch nach Error in toplevel
mov handle,ax
ret
@@fopen:
mov al,0
mov dx,offset temp_fname
DOS OPEN_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fwrite:
push ds
mov bx,handle
mov cx,0ffffh
mov ds,ovl_base
mov dx,0
DOS WRITE_TO_HANDLE
CALLC disp_disk_ful
pop ds
ret
; end fwrite
@@fread:
push ds
mov bx,handle
mov cx,0ffffh
mov ds,ovl_base
mov dx,0
DOS READ_FROM_HANDLE
CALLC disp_disk_err
pop ds
ret
; end fread
dos_shell endp
init_ramdisc proc
push es
PUSHR <ax,cx>
mov ax,ramdisklen ; in paragraphen
LOG2(64) ; nach cl (1024/16)
shr ax,cl ; ax:=ramdisk len in KB
and ax,0fff0h or 1100b ; um Vielfache von 4K zu bekommen
push ax
LOG2(4)
shr ax,cl ; ax/=4
mov lasttrack_rd,al ; Tracks zu 4 K
pop ax
; dsm=ramdisklen in K/2 -1 , da bls=2k
shr ax,1 ; geht klar, da sowieso vielfaches von 4
dec ax
cmp ax,255
jna short @@nobig
mov ax,255 ; mehr wie 510k geht im PC sowieso kaum
@@nobig:
mov dpb1.dsm,ax ; blockzahl
inc ax ; da KB=(dsm+1)*2
shl ax,1
mov ramdisklen,ax ; KBzahl
mov dpb1.bsh,4
mov dpb1.blm,15
mov dpb1.exm,1 ; blocksize=2048
mov dpb1.drm,127 ; dir entries
mov dpb1.al0,11000000b
; loesche ramdisc
mov ax,rdseg_adr
mov es,ax
mov ax,ramdisklen
@@clrloop:
cmp ax,64 ; k
jb short @@less64
push ax
mov cx,8000h ; do 32k words
mov di,0
mov ax,0e5e5h
rep stosw ; es:di := E5E5
mov ax,es
add ax,1000h
mov es,ax
pop ax
sub ax,64
jmp @@clrloop
@@less64:
LOG2(512)
shl ax,cl ; ax*=1024/2 (word)
mov cx,ax
mov di,0
mov ax,0e5e5h
rep stosw ; es:di := E5E5
POPR <cx,ax>
pop es
call load_ramdisc
ret
init_ramdisc endp
; speichere Ramdisk auf Platte
;
; der LOCAL Befehl legt lokale Auto-Vars auf dem Stack an mit
; Laenge AUTO_SIZE
;
save_ramdisc proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call ins_dos_disk
cmp ax,MESG_ESC
jz short @@exit
call @@fopen
jc short @@exit
call @@fwrite
call @@fclose
call ins_cpm_disk
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov cx,0
mov dx,offset ramdisc_fname
DOS CREATE_FILE
CALLC disp_disk_err
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fwrite:
; erst mal Ramdiskgroesse schreiben, beim Laden muss sie stimmen
mov bx,offset secbuf
mov ax,ramdisklen
mov [bx],ax
mov dx,bx
mov bx,handle
mov cx,2
DOS WRITE_TO_HANDLE
CALLC disp_disk_ful
mov bx,rdseg_adr
mov ax,ramdisklen
@@wrtloop:
cmp ax,0 ; fertig ?
; 0 oder negativ = Abbruch
jle short @@wrtexit
push ax
cmp ax,32 ; kilobyte
mov cx,8000h ; 32k
ja short @@more32
LOG2(1024) ; nach cl
shl ax,cl ; ax*=1024
mov cx,ax ; cx = Laenge in Byte
@@more32:
push bx
push ds
push bx
pop ds
mov bx,handle ; klappt, da ueber SS:BP
mov dx,0 ; offset
DOS WRITE_TO_HANDLE
CALLC disp_disk_ful
pop ds
pop bx
pop ax
add bx,800h ; segment um 32k weitersetzen
sub ax,32 ; restlaenge -=32
jmp @@wrtloop
@@wrtexit:
ret
; end fwrite
save_ramdisc endp
; hole Ramdisk von Platte
;
; der LOCAL Befehl legt lokale Auto-Vars auf dem Stack an mit
; Laenge AUTO_SIZE
;
load_ramdisc proc
LOCAL handle:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
call @@fopen
jc short @@exit ; nicht da
call @@fread
call @@fclose
@@exit:
mov sp,bp
pop bp
ret
@@fopen:
mov al,0
mov dx,offset ramdisc_fname
DOS OPEN_FILE
mov handle,ax
ret
@@fclose:
mov bx,handle
DOS CLOSE_FILE
CALLC disp_disk_err
ret
@@fread:
mov dx,offset secbuf
mov bx,handle
mov cx,2
DOS READ_FROM_HANDLE
CALLC disp_disk_err
mov bx,dx
mov bx,[bx]
cmp bx,ramdisklen
ja short @@rdexit ; ueberhaupt zu gross
mov ax,bx ; Laenge Ramdisk auf Platte
mov bx,rdseg_adr
@@rdloop:
cmp ax,0 ; fertig ?
jle short @@rdexit
push ax
cmp ax,32
mov cx,8000h ; 32k
ja short @@more32
LOG2(1024) ; nach cl
shl ax,cl ; ax*=1024
mov cx,ax ; cx = Laenge in Byte
@@more32:
push bx
push ds
push bx
pop ds
mov bx,handle ; klappt, da ueber SS:BP
mov dx,0 ; offset
DOS READ_FROM_HANDLE
CALLC disp_disk_err
pop ds
pop bx
pop ax
add bx,800h ; segment um 32k weitersetzen
sub ax,32 ; restlaenge -=32
jmp short @@rdloop
@@rdexit:
ret
; end fread
load_ramdisc endp
bios88 proc
;
; bios88 ist handler fuer alle bios calls
; In: al=bios nummer (warm boot = 1)
; cx,bx,dx wie bei cp/m bios calls
; Out: di,ah zerstoert
; si kann veraendert sein, falls bios change_si_flag
; hat nach wboot
;
; waehrend aller bios routinen zeigt ds auf emudata_seg
; es z80cpu_seg
mov di,emudata_seg
mov ds,di
mov z80_pc,si
cmp breakflag,TRUE
CALLZ prg_exit
PUSHR <bp,dx>
; cx nicht pushen, da cl:=drive # bei wboot
xor ah,ah ; bios # = index
sal ax,1
mov di,ax
call cs:biostab[di]
mov si,z80_pc
POPR <dx,bp>
cmp breakflag,TRUE
CALLZ prg_exit
mov di,z80seg_adr
mov ds,di
mov es,di
jmp bios88ret
bios88 endp
biostab label word
dw boot ; cold start
dw wboot ; warm start
dw constat ; console status
dw conin ; console character in
dw conout ; console character out
dw list ; list character out
dw punch ; punch character out
dw reader ; reader character out
dw home ; move head to home position
dw seldsk ; select disk
dw settrk ; set track number
dw setsec ; set sector number
dw setdma ; set dma address
dw read ; read disk
dw write ; write disk
dw listst ; return list status
dw sectran ; sector translate
rept 25-16-1
dw wboot
endm
dw cpm3_move ; move
dw cpm_get_time ; time
;
;
; individuelle UPs um jede Funktion auszufuehren
;
boot proc
;simplest case is to just perform parameter initialization
mov in_boot_flag,TRUE
mov bx,offset on_sign_string
call puts
call instzbios
cmp fmt_file_flg,TRUE
CALLZ load_disk_pars
call read_sys ; lade bdos aus file CPMSYS.CPM
pushf
call ins_cpm_disk
popf
jnc short @@goodload ; laden aus File klappte
call get_sys ; BDOS mit BIOS fkt von Diskette laden
@@goodload:
; Copyright von dr ausgeben
mov cl,27
call conout
mov cl,'k' ; pop cursor pos
call conout
mov cl,27
call conout
mov cl,'J' ; Rest vom Fenster loeschen
call conout
mov bx,offset cpm_bdos_buf+18h
call puts
mov bx,offset cr_lf_txt
call puts
;
; berechne adresse von ccp,bdos und bios
; dabei wird die JMP CCPENTRY Anweisung bei CCP+0 benutzt
;
mov di,offset cpm_bdos_buf+807h
mov al,[di]
cmp al,11h
jnz short @@no_standard
mov di,offset cpm_bdos_buf+1
mov ax,[di]
sub ax,35ch ; ax:=start ccp
; CP/M CCP? dann al jetzt 0
or al,al
jz short @@is_standard
; sonst defaultmaessig e400 annehmen
@@no_standard:
mov ax,0e400h
@@is_standard:
mov ccp_adr,ax
add ax,806h
mov bdos_adr,ax
add ax,(BDOSLEN-806h)
mov bios_adr,ax
call reset_conout
mov di,IOBYTE
mov byte ptr es:[di],0 ; clear IOBYTE
mov di,CDISK
mov byte ptr es:[di],0 ; select disk zero
call reset_punch_buf
call reset_reader_buf
mov in_boot_flag,FALSE
jmp wboot
boot endp
;
; setze Punch buffer auf leer
;
reset_punch_buf proc
mov punch_buf_entries,0
mov punch_buf_ptr,offset punch_buf
ret
reset_punch_buf endp
;
; setze Reader buffer auf nicht belegt
;
reset_reader_buf proc
mov reader_empty_flag,FALSE ; noch nicht lesen versucht
mov reader_buf_entries,0
mov reader_buf_ptr,offset reader_buf
mov reader_file_pointer,0
mov reader_file_pointer+2,0
ret
reset_reader_buf endp
;
; Stringausgabe
; In: bx zeigt auf String, Stringende = \0
;
puts proc
@@loop:
mov cl,[bx]
or cl,cl
jz short @@exit
inc bx
push bx
call conout
pop bx
jmp short @@loop
@@exit:ret
puts endp
instzbios proc
;
; installiere z80 bios
; und discparameter
;
PUSHR <si,di,cx>
mov si,offset zbiosbeg
mov di,ZBIOS
mov cx,zbiosend-zbiosbeg
cld
rep movsb ; es:di := ds:si
POPR <cx,di,si>
ret
instzbios endp
instjp proc
;
; installiere z80 bios Sprungleiste bei bios_adr
;
; register o.k.
;
; darf nicht aufgerufen werden, bevor bios_adr
; von boot aufgesetzt wurde
;
PUSHR <si,di,cx>
mov si,offset wboote-3
mov di,bios_adr
mov cx,zvecend-wboote+3
cld
rep movsb ; es:di := ds:si
POPR <cx,di,si>
ret
instjp endp
read_sys proc
;
; lade cp/m bdos aus file CPMSYS.CPM in buffer
; um es durch warm boot zu benutzen
;
; das File entspricht dem durch MOVCPM erzeugten Speicherauszug
; dabei beginnt das eingentlich System erst ab Offset 880h
MOVCPM_OFFS = 880H
PUSHR <ax,bx,cx,dx,si>
mov al,0
mov dx,offset cpmsys_fileName
DOS OPEN_FILE
jc short @@exit
mov si,ax
; setze Filepointer auf CCP Anfang im File
mov cx,0 ; cx:dx: offset
mov dx,MOVCPM_OFFS
mov al,0 ; offset from start
mov bx,si
DOS MOVE_FILE_POINTER
;
; emudata_seg:buffer := CPMSYS.CPM
;
mov cx,BDOSLEN ; Laenge
mov bx,si ; handle
mov dx,offset cpm_bdos_buf
DOS READ_FROM_HANDLE
jc short @@exit
mov bx,si
DOS CLOSE_FILE
CALLC disp_disk_err
@@exit:
POPR <si,dx,cx,bx,ax>
ret
read_sys endp
; System von SystemSpuren laden
; while sector in systemsectoren do
; lies sector
; if an Adresse String COPYRIGHT 1979 ..
; sys=naechste SYS_SECS sektoren
; lies sys
; exit
; mache Checksumme ueber sys
; if falsch
; "no sys"
; endif
; endif
; endwhile
; "no sys"
get_sys proc
LOCAL sys_recs,last_sys_rec:WORD = AUTO_SIZE
push bp
mov bp,sp
sub sp,AUTO_SIZE
@@get_sys_loop:
call login_disc
mov ah,0
mov al,dpb0.off-dpb0.spt ; off
call get_dpb_entrie ; nach bx
push bx
mov ah,0
mov al,dpb0.spt-dpb0.spt ; spt
call get_dpb_entrie ; nach bx
pop ax
mul bx ; ax:=system records
mov sys_recs,ax
mov cx,0
; im Systembereich nach String "COPYRIGHT (C) 1979 " suchen
@@srch_loop:
cmp cx,sys_recs
jnc short @@read_err ; schon ausserhalb sys
push cx ; sector
push cx
mov cx,DEFAULT_DMA
call setdma
pop cx
mov ah,cpm_drive
call set_abs_sect
call read
pop cx
cmp al,1
jz short @@read_err
call @@compare
jz short @@found
inc cx
jnz @@srch_loop
@@found:
mov bx,DEFAULT_DMA
push cx
add cx,SYS_SECS
mov last_sys_rec,cx
pop cx
@@loop:
push cx ; sector
push bx ; dma
push cx
mov cx,bx
call setdma
pop cx
mov ah,cpm_drive
call set_abs_sect
call read
pop bx
pop cx
cmp al,1
jz short @@read_err
add bx,RECORD_LEN
inc cx
cmp cx,last_sys_rec
jnz @@loop
jmp short @@good_load
@@read_err:
mov bx,offset no_sys
call puts
call conin
jmp @@get_sys_loop
@@good_load:
; System aus CP/M Segment nach Datensegment uebertragen
PUSHR <si,di,bx,cx,ax>
push ds
push es
mov di,offset cpm_bdos_buf
mov si,DEFAULT_DMA
mov ax,z80seg_adr
mov ds,ax
mov ax,emudata_seg
mov es,ax
mov cx,BDOSLEN/2
cld
rep movsw ; es:di := ds:si
pop es
pop ds
POPR <ax,cx,bx,di,si>
mov sp,bp
pop bp
ret
@@compare:
PUSHR <si,di,cx,ax>
mov si,offset bdos_str
mov di,DEFAULT_DMA+10H
mov cx,bdos_str_end-bdos_str
@@cp_loop:
mov al,[es:di]
mov ah,[si]
inc si
inc di
cmp al,ah
jnz short @@cp_exit
loop @@cp_loop
@@cp_exit:
POPR <ax,cx,di,si>
ret
get_sys endp
;
set_abs_sect proc ; absoluten Sektor in cx, drive ah setzen
push cx
mov al,0 ; spt
call get_dpb_entrie ; nach bx
pop ax
mov dx,0
div bx
push dx
mov cx,ax
call settrk
pop cx
call setsec
ret
set_abs_sect endp
wboot proc
push di
call copy_bdos
call instjp ; bios Sprungleiste installieren
;
; 0000h := JP WBOOT
; 0005h := JP BDOS
;
mov di,0 ; start cp/m memory
mov byte ptr es:[di+0],0c3h
mov bx,bios_adr
add bx,3 ; bx:=warmboot
mov es:[di+1],bx ; set address field for jmp at 0
;
mov byte ptr es:[di+5],0c3h
mov bx,bdos_adr ; bdos entry point
mov es:[di+6],bx ; address field of jump at 5 to bdos
;
mov cx,DEFAULT_DMA ; default dma address is 80h
call setdma
call login_disc ; physikalisches Login
;
mov ax,ccp_adr
mov z80_pc,ax ; jp ccp
mov di,CDISK
mov cl,es:[di] ; get current disk number & send to ccp
pop di
ret ;go to cp/m for further processing
copy_bdos:
PUSHR <si,di,cx>
mov di,ccp_adr
mov si,offset cpm_bdos_buf
mov cx,BDOSLEN/2
rep movsw ; es:di := ds:si
POPR <cx,di,si>
ret
wboot endp
;
;
;
constat proc
;
; console status, return 0ffh if character ready, 00h,Z_flag if not
;
; Out: al=keyboard status
;
push bx
mov ah,1 ; keybord status
int 16h
mov al,0ffh
jnz short @@exit
mov al,0
@@exit:
pop bx
ret
constat endp
; conin im Modul CONOUT
;
; test auf ^C, wenn ja, warm boot
;
tst_ctrl_c proc
push ax
call constat
jz short @@exit
call conin
cmp al,3 ; ^C
clc ; cy = no error
jnz short @@exit
mov bx,offset control_c_txt
call puts
stc
@@exit:
pop ax
ret
tst_ctrl_c endp
;
; list character in register c auf Drucker
;
list proc
call listst
jnz short @@ok
call tst_ctrl_c
jnc short list
mov z80_pc,0 ; Z80 macht ab Adresse 0 weiter
ret
@@ok:
mov al,cl ; char nach register a
xor dx,dx ; lpt 0
xor ah,ah ; list character
push bp
int 17h
pop bp
cmp ah,10h ; ok
jnz short list
ret
list endp
;
; return list status (0,Z_flag if not ready, 1 if ready)
;
listst proc
push dx
xor dx,dx ; lpt 0
mov ah,2 ; test printer
int 17h
mov al,1
cmp ah,90h ; on line und not busy
jz short @@lret
dec al
@@lret:
or al,al
pop dx
ret
listst endp
date_dos_2_cpm proc ; out: AX=tage seit 1.Jan. 1978
push bx
push cx
push dx
DOS GET_DATE
push dx
mov bx,1978
inc cx ; ein Jahr zuviel, für Test ob akt. Schaltjahr
MOV AX,0
@@add_loop:
cmp bx,cx ; aktuelles Jahr erreicht ?
jz short @@years_done
mov dx,bx
and dx,11b ; 0 => durch 4 teilbar
sub dx,1 ; 0 => cy
mov dx,365
adc dx,0 ; wird unten zum abziehen gebraucht
add ax,dx ; 0 => 366
inc bx
jmp @@add_loop
@@years_done:
sub ax,dx ; da ein Jahr oben zuviel, 365 oder 366 wieder weg
pop dx ; mon/tag
mov ch,0
mov cl,dl
add ax,cx ; tag
mov cl,dh ; monat
dec cl ; da aktueller Monat schon dran
jz short @@is_jan
mov bx,offset month_tab
@@add_months:
mov dl,[bx] ; verschieden lange monate
inc bx
mov dh,0
add ax,dx
loop @@add_months
@@is_jan:
pop dx
pop cx
pop bx
ret
date_dos_2_cpm endp
cpm3_move proc ; move wie bei cpm plus bios
push ds
push es
pop ds
mov si,bx
mov di,dx
add bx,cx ; hl=blockende
cmp bx,dx ; größer Ziel ?
jnb short @@topdown
cld
rep movsb
jmp short @@exit
@@topdown:
add si,cx
add di,cx
mov bx,si
mov dx,di
dec si
dec di
std
rep movsb
pop ds
ret
@@exit:
mov bx,si
mov dx,di
pop ds
ret
cpm3_move endp
cpm_get_time proc ; dos zeit nach cpm plus zeit
PUSHR <AX,CX,DX>
cmp cl,0
jnz short @@exit ; set time ignorieren
call date_dos_2_cpm
push ds
push es
pop ds
mov @date+ZBIOSDISPL,ax
DOS GET_TIME
mov dl,10
mov al,cl
mov ah,0 ; sonst divide overflow
div dl
mov cl,4
shl al,cl
or al,ah
mov @min+ZBIOSDISPL,al
mov al,ch
mov ah,0
div dl
shl al,cl
or al,ah
mov @hour+ZBIOSDISPL,al
mov al,dh
mov ah,0
div dl
shl al,cl
or al,ah
mov @sec+ZBIOSDISPL,al
pop ds
mov bx,(offset @date)+ZBIOSDISPL
@@exit:
POPR <DX,CX,AX>
ret
cpm_get_time endp
del_punch_file proc
mov dx,offset punch_fileName
DOS DELETE_FILE
ret
del_punch_file endp
;
; punch character in register c
; wird durch Output in File auf MsDos Disk ersetzt
; Buffer wird auf File geschrieben, falls er voll ist
; und auch am Ende des Emulator-
; laufs
;
punch proc
;
; fill buffer until full or eof
; open file
; if not exist file
; create file
; endif
; go to end of file
; append character
; close file
; empty buffer
;
; falls punch von bios88 aufgerufen wurde, ist al<>0
; nach Aufruf von wrt_out_punch aber al=0
PUSHR <ax,bx,cx,dx,si>
cmp al,0
jz short @@write_out
mov bx,punch_buf_ptr
mov [bx],cl ; buf:=character
inc bx
mov punch_buf_ptr,bx
inc punch_buf_entries
cmp punch_buf_entries,PUNCH_BUF_SIZE
jnz short @@exit
@@write_out:
call ins_dos_disk
cmp ax,MESG_ESC
jz short @@exit
mov al,1
mov dx,offset punch_fileName
DOS OPEN_FILE
jnc short @@file_found
mov cl,0 ; write only
DOS CREATE_FILE
CALLC disp_disk_err
@@file_found:
mov bx,ax ; handle
mov cx,0
mov dx,0
mov al,2 ; offset from end
DOS MOVE_FILE_POINTER
mov cx,punch_buf_entries
mov dx,offset punch_buf
DOS WRITE_TO_HANDLE
CALLC disp_disk_ful
DOS CLOSE_FILE
call reset_punch_buf
call ins_cpm_disk
@@exit:
POPR <si,dx,cx,bx,ax>
ret
punch endp
wrt_out_punch proc
cmp punch_buf_entries,0
jz short @@exit
xor al,al
call punch
@@exit:
ret
wrt_out_punch endp
;
;
reader proc ;read character into register a from reader device
; if buffer empty
; open file
; if file not present
; exit
; else
; fill buffer
; endif
; endif
; read char
cmp reader_empty_flag,TRUE
jz short @@empty
cmp reader_buf_entries,0
jnz short @@do_read
; Buffer fuellen
mov al,0
mov dx,offset reader_fileName
DOS OPEN_FILE
jc short @@empty
push ax
mov bx,ax
mov dx,reader_file_pointer
mov cx,reader_file_pointer+2
PUSHR <dx,cx>
add dx,READER_BUF_SIZE
adc cx,0
mov reader_file_pointer,dx
mov reader_file_pointer+2,cx
POPR <cx,dx>
mov al,0 ; offset from start
DOS MOVE_FILE_POINTER
pop bx ; handle
mov cx,READER_BUF_SIZE
mov dx,offset reader_buf
DOS READ_FROM_HANDLE
push ax
DOS CLOSE_FILE
pop ax
cmp ax,0
jz short @@empty
; buffer erfolgreich gefuellt
mov reader_empty_flag,FALSE
mov reader_buf_entries,ax
mov reader_buf_ptr,offset reader_buf
@@do_read:
mov bx,reader_buf_ptr
mov al,[bx]
inc bx
mov reader_buf_ptr,bx
dec reader_buf_entries
jmp short @@exit
@@empty:
mov reader_empty_flag,TRUE
mov al,CPM_EOF
mov bx,0ffffh ; hl=FFFF == Dos File Ende
ret
@@exit:
mov bx,0 ; a=hl=gelesenes byte
mov bl,al
ret
reader endp
; In: ah=drive
; al=offset im dpb
; Out: bx=word dpb[al]
get_dpb_entrie proc
push si
mov bl,ah
call get_dph_adr ; bx:=dph
xor ah,ah
mov si,ax
mov bx,[es:bx+10] ; bx:=dpb
mov bx,[es:bx+si] ; bx:=dpb[ax]
pop si
ret
get_dpb_entrie endp
;
; select disk in register c
;
seldsk proc
cmp cl,NDISKS-1
mov bx,0000h ; 0 = Drive nicht existent
ja short @@exit
mov diskno,cl
mov bl,cl
call get_dph_adr
@@exit:
ret
seldsk endp
; In: bl:=Drive #
; Out: bx:= disk parameter header address
;
get_dph_adr proc
mov bh,0 ; high order zero
push cx
LOG2(16) ; nach cl
sal bx,cl ; bx*=16 (size of each header)
pop cx
add bx,offset dph_base+ZBIOSDISPL
ret ;hl=.dph_base(diskno*16)
get_dph_adr endp
;
; move to the track 00 position of current drive
; translate this call into a settrk call with parameter 00
;
home proc
mov cx,0 ; select track 0
call settrk
mov home_flag,TRUE ; zur Auswertung durch read_track
ret
home endp
;
; set track given by register bc
;
settrk proc
mov word ptr track,cx
ret
settrk endp
;
; set sector given by register bc
;
setsec proc
mov word ptr sector,cx
ret
setsec endp
;
;translate the sector given by bc using the
;translate table given by de
;
sectran proc
mov bx,cx
or dx,dx ; table ptr == 0 ?
jz short @@exit
add cx,cx ; index *= 2
mov bx,dx ; hl=trans
add bx,cx
mov bx,[es:bx] ;hl=trans[sector]
@@exit:
ret ;with value in hl
sectran endp
;
; set dma address given by registers b und c
;
setdma proc
mov dmaad,cx
ret
setdma endp
calcadr proc
;
; ax:bx := record adresse in Ramdisk
; illegal sec: cy
;
; 1. 64k track 0..15
; 2. 64k track 16..31
; ..
mov al,track
cmp al,lasttrack_rd
ja short @@badrec
mov al,sector
cmp al,LAST_SEC1
ja short @@badrec
; phys recordnum = track * spt + sector
PUSHR <cx,dx>
mov ah,RMD
mov al,0 ; spt
call get_dpb_entrie
mov cx,bx ; cx:=spt B:
mov ax,word ptr track
mul cx
add ax,word ptr sector ; ax:=phys record num
mov cx,512 ; 64K = 512 records @ 128 Bytes
mov dx,0
div cx ; ax:= # of 64K, dx:=recordnum
mov cx,1000h
push dx
mul cx
pop dx
mov bx,rdseg_adr
add bx,ax
mov ax,dx
LOG2(RECORD_LEN)
shl ax,cl
xchg bx,ax
POPR <dx,cx>
clc
@@exit:
ret
@@badrec:
stc
jmp short @@exit
calcadr endp
;
read proc
;
; lies einen record von 128 bytes
;
cmp diskno,RMD
jnz short @@rfdisc
call calcadr ; ax:bx := record addresse
jc short @@badsec
call rdrec
@@exit:xor al,al ; o.k.
ret
@@badsec:
mov al,1
ret
@@rfdisc:
call is_valid_rec ; ax=rec/sec
jc short @@badsec
call read_track
jc short @@badsec
;
; record num ist in ah
; ax:bx := adresse Track Buffer
;
mov al,READ_OP
call get_adr_in_buf ; nach bx
mov ax,ds ; ax := seg track buffer
call rdrec
jmp short @@exit
read endp
; berechne Sektoradresse im Buffer
; a=sector*128+buf
; input: al=READ_OP / WRITE_OP
get_adr_in_buf proc
PUSHR <AX,CX>
call is_dir_track
mov bx,offset dirtrbuf
jz short @@calc
mov bx,offset secbuf
cmp al,READ_OP
jz short @@calc
mov bx,offset wrtbuf
@@calc:
xor ax,ax
mov al,sector
LOG2(RECORD_LEN) ; nach cl
shl ax,cl ; ax *= 128
add bx,ax
POPR <CX,AX>
RET
get_adr_in_buf endp
is_dir_track proc
push ax
mov al,track
cmp al,dirtrack
pop ax
ret
is_dir_track endp
;
;
;
is_valid_rec proc
; Out: cy sector illegal
;
; test auf gueltigen track/sector auf DISC
;
COMMENT *
mov al,track
cmp al,LAST_TRCK0
stc
ja short @@exit
mov al,sector
cmp al,LAST_SEC0
stc
ja short @@exit
; ok, clear cy
*
clc
@@exit:
ret
is_valid_rec endp
;
; lies ganzen Track, falls noch nicht im Speicher
;
read_track proc
;
; Error: cy=1
;
PUSHR <ax,bx,cx,dx>
; der DirTrack wird nur gelesen nach HOME
mov al,track
cmp al,dirtrack
jnz short @@nodir
cmp home_flag,TRUE
mov home_flag,FALSE
mov bx,offset dirtrbuf
jz short @@read_dir_track
clc
jmp short @@exit
@@nodir:
cmp al,last_track_read
jz short @@exit
mov bx,offset secbuf
mov last_track_read,al
@@read_dir_track:
mov r_track,al
call read_phys_track
; ab jetzt CY nicht mehr veraendern !
@@exit:
POPR <dx,cx,bx,ax>
ret
read_track endp
;
; lies ganzen Track, in den geschrieben werden soll, ein,
; falls noch nicht im Speicher
;
read_w_track proc
;
; Error: cy=1
;
PUSHR <ax,bx,cx,dx>
; der DirTrack ist sowieso im Speicher und braucht
; nicht gelesen werden
mov al,track
cmp al,dirtrack
jz short @@exit
cmp al,last_track_written
jz short @@exit
; falls noetig alten Track im WriteBuffer erst
; ausschreiben
call write_out_old_track
mov al,track
mov bx,offset wrtbuf
mov last_track_written,al
mov r_track,al
call read_phys_track
; ab jetzt CY nicht mehr veraendern !
@@exit:
POPR <dx,cx,bx,ax>
ret
read_w_track endp
; physikalische Sektorlaenge herstellen
dos_phys_len proc
PUSHR <es,ax,di>
mov ax,0
mov es,ax
les di,[es:78h]
mov al,ms_phys_sec_len
mov es:[di+3],al
POPR <di,ax,es>
ret
dos_phys_len endp
; physikalische Sektorlaenge herstellen
cpm_phys_len proc
PUSHR <es,ax,di>
mov ax,0
mov es,ax
les di,[es:78h]
mov al,cpm_phys_sec_len
mov es:[di+3],al
POPR <di,ax,es>
ret
cpm_phys_len endp
normalize_pointer proc ; es:bx
PUSHR <cx,ax>
mov ch,bl
and ch,0fh
LOG2(16) ; NACH CL
shr bx,cl ; bx:=16
mov ax,es
add ax,bx
mov es,ax
mov bx,0
mov bl,ch
POPR <ax,cx>
ret
normalize_pointer endp
read_phys_track proc
PUSHR <ax,bx,cx,dx>
; der naechste Test sollte eigentlich nicht noetig sein, aber
; mein 386 Ami Bios weigert sich sonst, den gleichen Track nochmal
; zu lesen; jedenfalls kann auch gleich ein Diskwechsel festgestellt
; werden
call tst_disk_change
jz short @@no_change
mov last_track_read,0ffh
@@no_change:
mov cl,retry_count
xor ch,ch
@@tryread:
call cpm_phys_len
push es
mov ax,emudata_seg
mov es,ax ; ist sonst z80cpu_seg
push cx
mov dl,cpm_drive ; diskno
mov dh,side
mov ch,r_track
call track_trans
mov cl,first_phys_sec ; physical sector number
mov al,phys_sec_pt ; lies ganzen Track auf einmal
mov ah,2 ; read sector
; call normalize_pointer
int 13h ; es:bx := track
pop cx
pop es
call dos_phys_len
jnc short @@exit
cmp ah,9
jz dma_bound
; Fehler, Disk Reset und nochmal versuchen
mov ah,0
mov dl,cpm_drive
int 13h ; disk reset
loop @@tryread
stc ; Error, cy
@@exit:
POPR <dx,cx,bx,ax>
ret
read_phys_track endp
; Trackuebersetzung nur fuer CP/M 86 DS DD noetig
; muss vor phys. read/write Ops aufgerufen werden
;
; in: ch: zu uebersetzender Track
; dh: Disk Side
track_trans proc
cmp ch,phys_tracks
jb short @@exit
cmp cpm86dd_flag,TRUE
jnz short @@nocpm86dd
push ax
mov al,ch
mov ch,79
sub ch,al
pop ax
inc dh ; Seite 1
jmp short @@exit
@@nocpm86dd:
sub ch,40
inc dh ; Seite 1
@@exit:
ret
track_trans endp
tst_disk_change proc
cmp hd_drive_flag,FALSE
jz short @@exit ; kein Diskwechsel => Z
mov ah,16h
mov dl,cpm_drive
int 13h ; Test auf Wechsel
cmp ah,0 ; kein Diskwechsel => Z
@@exit:
ret
tst_disk_change endp
; falls noch nicht geschriebener Track im Speicher ist,
; der kein DirTrack ist, ausschreiben
write_out_old_track proc
cmp write_flag,TRUE
jnz short @@exit
mov al,last_track_written
mov w_track,al
mov outbuf_ptr,offset wrtbuf
call write_phys_track
mov write_flag,FALSE
@@exit:
ret
write_out_old_track endp
;
; den Dir Track auf Disk schreiben
;
write_out_dir proc
call write_out_old_track ; falls noetig
mov al,dirtrack
mov w_track,al
mov outbuf_ptr,offset dirtrbuf
call write_phys_track
ret
write_out_dir endp
write_phys_track proc
PUSHR <ax,bx,cx,dx>
mov cl,retry_count
xor ch,ch
@@trywrit:
call cpm_phys_len
push es
mov ax,emudata_seg
mov es,ax
push cx
mov dl,cpm_drive ; diskno
mov dh,side
mov ch,w_track
call track_trans
mov cl,first_phys_sec ; physical sector number
mov bx,outbuf_ptr
mov al,phys_sec_pt
; call normalize_pointer
mov ah,3 ; read sector
int 13h ; track := es:bx
pop cx
pop es
call dos_phys_len
jnc short @@exit
cmp ah,9
jz dma_bound
mov ah,0
mov dl,cpm_drive
int 13h ; disk reset
loop @@trywrit
stc ; Error, cy
@@exit:
POPR <dx,cx,bx,ax>
ret
write_phys_track endp
;
; versuchen, richtiges physikalisches Format zu finden
; und disc parameter block herrichten
;
; falls autologin_flag = FALSE , kann das Zeitaufwendige login
; entfallen
login_disc proc
push bx
call tst_hd_drive
mov last_track_read,0ffh ; Track Buffer auf leer setzen
; damit bei read_track gelesen wird
mov write_flag,FALSE ; sonst wird beim naechsten write
; erst alter Schrott ausgeschrieben
cmp autologin_flag,TRUE
jnz @@exit
mov cpm_drive,PHYS_DRV
mov cpm86dd_flag,FALSE
mov retry_count,2 ; Leseversuche herabsetzen,
; damit bei falschem
; Format nicht zuviel Zeit verschwendet wird
@@loop:
cmp breakflag,TRUE
CALLZ prg_exit
; erst mal mit dem zuletzt verwendeten Format versuchen
; aber nicht, wenn letztes CP/M 86 war, da sonst das
; aehnliche Format mit 9 Sektoren nicht erkannt wird
cmp byte ptr phys_sec_pt,8
jz short @@tst_cpm86
mov al,pspt_last
mov phys_sec_pt,al
mov al,phys_tracks_last
mov phys_tracks,al
mov al,frstps_last
mov first_phys_sec,al ; physical sector
mov al,dirtr_last
mov dirtrack,al
inc al
mov r_track,al
mov bx,offset secbuf
call read_phys_track
mov bx,dpb_last
jnc @@goodfmt
;
; CP/M 86 format ?
;
; Bedingungen: Seite 0 muss Sektoren 1-8 haben, aber nicht 1-9
; Seite 1 muss Sektoren 1-8 haben
; setze 9 Phys. Sektoren, bei cp/m 86 mit 8 Sektoren pro Track muss
; es einen Lesefehler geben
@@tst_cpm86:
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,1h
mov r_track,1+1
mov dirtrack,1
mov bx,offset secbuf
call read_phys_track
jnc short @@nocpm86
mov phys_sec_pt,8
mov phys_tracks,40
mov first_phys_sec,1h
mov r_track,1+1
mov dirtrack,1
mov bx,offset secbuf
call read_phys_track
jc short @@nocpm86
; Teste noch Seite 2 mit SektorNummer > 39
mov phys_sec_pt,8
mov phys_tracks,40
mov first_phys_sec,1h
mov r_track,40
mov dirtrack,1
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_cpm86DS
mov cpm86dd_flag,TRUE
jnc @@goodfmt
mov cpm86dd_flag,FALSE
mov bx,offset dpb_cpm86SS
jmp @@goodfmt
@@nocpm86:
;
; cpc system format ?
;
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,41h
mov r_track,2+1
mov dirtrack,2
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_cpc
jnc short @@goodfmt
;
; data format ?
;
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,0c1h
mov r_track,0+1
mov dirtrack,0
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_dta
jnc short @@goodfmt
@@tsteigfrm:
;
; is it own format
;
mov phys_sec_pt,9
mov phys_tracks,40
mov first_phys_sec,1
mov r_track,0+1
mov dirtrack,0
mov bx,offset secbuf
call read_phys_track
mov bx,offset dpb_eig
jnc short @@goodfmt
@@badfmt:
mov bx,offset bad_format
call puts
call conin
cmp autologin_flag,TRUE ; waehrend conin kann Edit DPH gewes. sein
jnz short @@exit
jmp @@loop
@@goodfmt:
; physikalisches Format merken
mov al,phys_sec_pt
mov pspt_last,al
mov al,first_phys_sec
mov frstps_last,al
mov al,dirtrack
mov dirtr_last,al
mov dpb_last,bx
mov si,bx
mov di,offset dpb0+ZBIOSDISPL
mov cx,size dpb
cld
rep movsb ; es:di := ds:si
@@exit:
pop bx
mov retry_count,5
ret
login_disc endp
write proc
;
; write a record of 128 bytes
;
cmp diskno,RMD
jnz short wtodisc
call calcadr ; ax:bx := record adress
jc short @@bad
call wrtrec ; buffer := record
jc short @@bad
xor al,al ; write ok
ret
@@bad: mov al,1
ret
write endp
wtodisc proc
push cx
call is_valid_rec ; ax=rec/sec
pop cx
jc short @@badsec
; if track noch nicht im Buffer
; if noch alter Track im Buffer
; alten Track ausschreiben
; endif
; lies neuen Track ein
; endif
; if track ist dirtrack
; if noch alter Track im Buffer
; alten Track ausschreiben
; endif
; dirtrack ausschreiben
; else
; Setze Flag, daß Sektor geschreiben wurde
; endif
;
push cx ; bdos info, ob dirsectoren
call read_w_track
pop cx
jc short @@badsec
;
; record # ist in ah
;
push cx
mov al,WRITE_OP
call get_adr_in_buf
mov ax,ds ; ax:= seg track buffer
call wrtrec
pop cx
cmp cl,1
jz short @@isdir1
call is_dir_track
jz short @@isdir1
mov write_flag,TRUE
mov al,track
mov last_track_written,al
mov al,0 ; o.k.
jmp short @@exit
@@isdir1:
call write_out_dir
jc short @@badsec
mov al,0
jmp short @@exit
@@badsec:
mov al,1
@@exit:
ret
wtodisc endp
;
; In: ax:bx = source adr
;
rdrec proc
;
PUSHR <si,di,bx,cx,ax>
push ds
mov si,bx
mov di,dmaad ; ds MUSS = emudata_seg sein
mov ds,ax
mov cx,RECORD_LEN/2 ; record laenge in words
cld
rep movsw ; es:di := ds:si
pop ds
POPR <ax,cx,bx,di,si>
ret
rdrec endp
;
; In: ax:bx=target adr
;
wrtrec proc
PUSHR <si,di,cx,ax>
push es
push ds
mov si,dmaad ; ACHTUNG: der MOV muss
; vor DS Segmentwechsel sein
mov di,bx
mov bx,ax ; ramdisc
mov ax,es ; cp/m
mov es,bx
mov ds,ax
mov cx,RECORD_LEN/2 ; record laenge in words
cld
rep movsw ; es:di := ds:si
pop ds
pop es
POPR <ax,cx,di,si>
ret
wrtrec endp
emulator_seg ends
end init